﻿using System.Diagnostics.Contracts;

namespace System.IO
{
    /// <summary>Класс методов расширений для объектов класса System.IO.FileInfo</summary>
    public static class FileInfoExtentions
    {
        /// <summary>Скопировать файл в дирректорию</summary>
        /// <param name="SourceFile">Файл источник</param>
        /// <param name="DestinationDirectory">Дирректория назначения</param>
        /// <returns>Файл копия</returns>
        [Diagnostics.DebuggerStepThrough]
        public static FileInfo CopyTo(this FileInfo SourceFile, DirectoryInfo DestinationDirectory)
        {
            Contract.Requires(SourceFile != null);
            Contract.Requires(DestinationDirectory != null);

            return SourceFile.CopyTo(string.Format(@"{0}\{1}", DestinationDirectory.FullName, Path.GetFileName(SourceFile.Name)));
        }

        /// <summary>Скопировать файл в дирректорию</summary>
        /// <param name="SourceFile">Файл источник</param>
        /// <param name="DestinationDirectory">Дирректория назначения</param>
        /// <param name="Owerride">Перезаписать в случае наличия файла</param>
        /// <returns>Файл копия</returns>
        [Diagnostics.DebuggerStepThrough]
        public static FileInfo CopyTo(this FileInfo SourceFile, DirectoryInfo DestinationDirectory, bool Owerride)
        {
            Contract.Requires(SourceFile != null);
            Contract.Requires(DestinationDirectory != null);

            var newFile = string.Format(@"{0}\{1}", DestinationDirectory.FullName, Path.GetFileName(SourceFile.Name));
            if(!Owerride && File.Exists(newFile)) return new FileInfo(newFile);
            return SourceFile.CopyTo(newFile, true);
        }

        /// <summary>Скопировать файл</summary>
        /// <param name="SourceFile">Файл источник</param>
        /// <param name="DestinationFile">Файл копия</param>
        [Diagnostics.DebuggerStepThrough]
        public static void CopyTo(this FileInfo SourceFile, FileInfo DestinationFile)
        {
            Contract.Requires(SourceFile != null);
            Contract.Requires(DestinationFile != null);

            SourceFile.CopyTo(DestinationFile.FullName);
        }

        /// <summary>Скопировать файл</summary>
        /// <param name="SourceFile">Файл источник</param>
        /// <param name="DestinationFile">Файл копия</param>
        /// <param name="Owerride">Перезаписать в случае наличия файла</param>
        [Diagnostics.DebuggerStepThrough]
        public static void CopyTo(this FileInfo SourceFile, FileInfo DestinationFile, bool Owerride)
        {
            Contract.Requires(SourceFile != null);
            Contract.Requires(DestinationFile != null);

            SourceFile.CopyTo(DestinationFile.FullName, Owerride);
        }

        /// <summary>Получить имя файла без расширения</summary>
        /// <param name="file">Файл</param>
        /// <returns>Имя файла без расширения</returns>
        [Diagnostics.DebuggerStepThrough]
        public static string GetFileNameWithoutExtension(this FileInfo file)
        {
            Contract.Requires(file != null);
            Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()));

            return Path.GetFileNameWithoutExtension(file.Name);
        }

        /// <summary>Записать массив байт в файл</summary>
        /// <param name="file">Файл данных</param>
        /// <param name="Data">Данные</param>
        /// <param name="Append">Если истина, то данные будут добавлены в конец файла</param>
        [Diagnostics.DebuggerStepThrough]
        public static void WriteAllBytes(this FileInfo file, byte[] Data, bool Append = false)
        {
            Contract.Requires(file != null);
            Contract.Requires(Data != null);

            using(var fs = new FileStream(file.FullName,
                Append ? FileMode.Append : FileMode.Create,
                FileAccess.Write,
                FileShare.Read))
                fs.Write(Data, 0, Data.Length);
        }

        /// <summary>Записать все данные из потока в файл</summary>
        /// <param name="file">Файл данных</param>
        /// <param name="DataStream">Поток - источник данных</param>
        /// <param name="BufferSize">Размер буфера чтения по умолчанию 1024 байта</param>
        /// <param name="Append">Флаг добавления данных в конец файла</param>
        /// <param name="CompliteHandler">
        /// Обработчик текущего положения коретки чтения данных из потока. 
        /// Вызывается после чтения данных в буфер и до помещения их в файл.
        /// Должен вернуть истину, что бы данные были переданы в файл и процесс был продолжен.
        /// </param>
        /// <param name="OnComplite">Обработчик события завершения процесса записи данных</param>
        [Diagnostics.DebuggerStepThrough]
        public static void WriteAllBytes(this FileInfo file, Stream DataStream,
            int BufferSize = 1024, bool Append = false, Func<long, byte[], bool> CompliteHandler = null,
            EventHandler<EventArgs<FileInfo, Stream>> OnComplite = null)
        {
            Contract.Requires(file != null);
            Contract.Requires(DataStream != null);
            Contract.Requires(DataStream.CanRead);

            var buffer = new byte[BufferSize];
            var write = true;
            using(var lv_FileStream = new FileStream(file.FullName,
                Append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read))
                do
                {
                    var readed = DataStream.Read(buffer, 0, BufferSize);
                    if(CompliteHandler != null) write = CompliteHandler(DataStream.Position, buffer);
                    if(write && readed != 0) lv_FileStream.Write(buffer, 0, readed);
                    else write = false;
                } while(write);
            OnComplite.Start(file, new EventArgs<FileInfo, Stream>(file, DataStream));
        }

        /// <summary>Получить объект наблюдения за файлом</summary>
        /// <param name="file">Наблюдаемый файл</param>
        /// <returns>Объект наблюдатель</returns>
        [Diagnostics.DebuggerStepThrough]
        public static FileSystemWatcher GetWacher(this FileInfo file)
        {
            Contract.Requires(file != null);
            Contract.Ensures(Contract.Result<FileSystemWatcher>() != null);


            return new FileSystemWatcher(file.FullName);
        }

    }
}